#pragma once

#include<unordered_map>
#include<functional>
#include<iostream>
#include<cerrno>
#include<unistd.h>
#include<cstdlib>
#include<cstring>
#include<strings.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include"RingQueue.hpp"
#include"LockGuard.hpp"
#include"Thread.hpp"
#include"err.hpp"

namespace nt_server
{       
    static const uint16_t default_port=8888;//后续要服务端要绑定的端口号

    class UdpServer
    {
    public:
        UdpServer(uint16_t port=default_port)
        :_port(port)
        {
            //初始化互斥锁
            pthread_mutex_init(&_mtx,nullptr);

            //创建线程
            _producer=new Thread(1,std::bind(&UdpServer::RecvMessage,this));
            _consumer=new Thread(2,std::bind(&UdpServer::BroadcastMessage,this));
        }
        
        ~UdpServer()
        {
            //等待线程
            _producer->join();
            _consumer->join();

            pthread_mutex_destroy(&_mtx);

            delete _producer;
            delete _consumer;
        }

        void InitServer()
        {
            //1.创建套接字，成功返回文件描述符位置，失败返回-1
            _sock=socket(AF_INET,SOCK_DGRAM,0);//网络通信，udp协议

            if(_sock==-1)
            {
                std::cout<<"Create Socket Fail"<<strerror(errno)<<std::endl;
                exit(SOCKET_ERR);
            }

            std::cout<<"Create Socket Success:"<<_sock<<std::endl;

            //2.绑定IP地址和端口号
            struct sockaddr_in local;
            bzero(&local,sizeof(local));

            //填充字段
            local.sin_family=AF_INET;//设置为网络通信
            local.sin_port=htons(_port);//将uint16_t整形的主机序列转成网络序列
            //local.sin_addr.s_addr=inet_addr(_ip.c_str());//将点分十进制的字符串形式的主机序列转成网络序列
            local.sin_addr.s_addr=INADDR_ANY;

            //成功返回0，失败返回-1
            if(bind(_sock,(const sockaddr*)&local,sizeof(local)))
            {
                std::cout<<"Bind IP&&Port Fail:"<<strerror(errno)<<std::endl;
                exit(BIND_ERR);
            }
            
            std::cout<<"Bind IP&&Port Success"<<std::endl;

            //启动线程
            _producer->run();
            _consumer->run();
        }

        void RecvMessage()
        {
            char buff[1024];
            while(true)
            {
                //1.构造客户端结构体，recvfrom完善客户端信息
                struct sockaddr_in peer;
                socklen_t len=sizeof(peer);

                ssize_t n=recvfrom(_sock,buff,sizeof(buff)-1,0,(struct sockaddr*)&peer,&len);
                if(n>0)
                    buff[n]='\0';
                else 
                    continue;

                //2.处理数据，将peer的网络序列信息转化为主机序列
                std::string clientIp=inet_ntoa(peer.sin_addr);//获取IP地址
                uint16_t clientPort=ntohs(peer.sin_port);//获取端口号
                printf("Server get message from [%s:%d]: %s\n",clientIp,clientPort,buff);

                //3.判断是否添加该用户
                std::string user=clientIp+"-"+std::to_string(clientPort);
                {
                LockGuard lockguard(&_mtx);
                if(_userTable.count(user)==0)
                    _userTable[user]=peer;//首次出现，添加至哈希表中
                }

                //4.接受信息后将信息添加至环形队列
                std::string msg = "[" + clientIp + ":" + std::to_string(clientPort) + "] say# " + buff;
                _rq.Push(msg);

                
                
            }
        }

        void BroadcastMessage()
        {
            //将环形队列的消息取出并发给用户
            while(true)
            {
                //1.从环形队列中获取消息
                std::string msg;
                _rq.Pop(&msg);

                //2.将消息发给用户
                std::vector<sockaddr_in> arr;
                
                {
                    LockGuard lockguard(&_mtx);
                    for(auto &user:_userTable)
                        arr.push_back(user.second);
                }

                for(auto &addr:arr)
                {
                    sendto(_sock,msg.c_str(),msg.size(),0,(const sockaddr*)&addr,sizeof(addr));
                }

                

               
            }
        }

       
    private:
        pthread_mutex_t _mtx;
        int _sock;//套接字（文件描述符）
        uint16_t _port;//端口号
        wbj::RingQueue<std::string> _rq;
        std::unordered_map<std::string, struct sockaddr_in> _userTable;
        Thread* _producer;
        Thread* _consumer;
    };

    
}